﻿#include "unrealircd.h"


static CURLM *cm;
int maxRequest = 20;//最大请求连接
int request_cnt = 0;
HttpRequestList request_list_ready;
HttpRequestList request_list_process;


/*
初始化httpRequest
*/
void init_HttpRequest() {
	cm = curl_multi_init();
	curl_multi_setopt(cm, CURLMOPT_MAXCONNECTS, (long)maxRequest);
	init_HttpRequestList(&request_list_ready);
	init_HttpRequestList(&request_list_process);
	request_cnt = 0;

}
void init_HttpRequestList(HttpRequestList *list) {
	list->head.pre = NULL;
	list->head.next = &(list->end);
	list->end.pre = &(list->head);
	list->end.next = NULL;
	list->head.data = NULL;
	list->end.data = NULL;
}
/*
初始化HttpRequestData
*/
void init_HttpRequestData(HttpRequestData *data) {
	data->error = NULL;
	init_response(data);
	data->finish = 0;
	data->eh = NULL;
}
/*
添加数据到request_list_ready链表中
*/
void add_httpRequestReady(HttpRequestData *requestData) {
	init_HttpRequestData(requestData);
	add_httpRequestList(requestData, &request_list_ready);
}

/*
添加一个http请求到链表中
*/
void add_httpRequestList(HttpRequestData *requestData, HttpRequestList *list) {
	HttpRequestNode *requestNode = malloc(sizeof(HttpRequestNode));
	requestNode->data = requestData;
	requestNode->pre = list->end.pre;
	requestNode->pre->next = requestNode;
	list->end.pre = requestNode;
	requestNode->next = &(list->end);
}
/*
完成一个http请求，删除http请求节点
*/
void delet_httpRequestList(HttpRequestNode *requestNode) {
	if (requestNode->pre == NULL || requestNode->next == NULL) {
		return;
	}
	requestNode->pre->next = requestNode->next;
	requestNode->next->pre = requestNode->pre;
	free(requestNode);
}
/*
将节点转化位置
*/
void remove_httpRequestList(HttpRequestNode *requestNode, HttpRequestList *destination) {
	if (requestNode->pre == NULL || requestNode->next == NULL) {
		return;
	}
	requestNode->pre->next = requestNode->next;
	requestNode->next->pre = requestNode->pre;

	requestNode->pre = destination->end.pre;
	requestNode->pre->next = requestNode;
	destination->end.pre = requestNode;
	requestNode->next = &(destination->end);
}

/*
将未开始的请求加入到请求集中，如果http最大连接数不超过maxRequest，则不加入
*/
void httprequest_process() {
	
	for (; request_cnt < maxRequest; request_cnt++) {
		if ((request_list_ready.head.next) == &(request_list_ready.end)) {
			break;
		}
		HttpRequestNode * requestNode = request_list_ready.head.next;
		if(requestNode){
			do_http(requestNode->data);
			remove_httpRequestList(requestNode, &request_list_process);
		}
	}
	int running_handles = 0;
	curl_multi_perform(cm, &running_handles);
}

/*
执行一个http请求
*/
void do_http(HttpRequestData *requestData) {
	CURL *eh = curl_easy_init();
	requestData->eh = eh;
	prepare_http_head(eh, requestData);
	curl_easy_setopt(eh, CURLOPT_URL, requestData->url);
	switch (requestData->requestType) {
	case 0:
		curl_easy_setopt(eh, CURLOPT_CUSTOMREQUEST, "GET");
		break;
	case 1:
		curl_easy_setopt(eh, CURLOPT_CUSTOMREQUEST, "POST");
		curl_easy_setopt(eh, CURLOPT_POST, 1L);
		break;
	case 2:
		curl_easy_setopt(eh, CURLOPT_CUSTOMREQUEST, "PUT");
		break;
	case 3:
		curl_easy_setopt(eh, CURLOPT_CUSTOMREQUEST, "DELETE");
		break;
	}
	if(requestData->data){
		curl_easy_setopt(eh, CURLOPT_POSTFIELDS, requestData->data);
	}
	curl_easy_setopt(eh, CURLOPT_WRITEDATA, requestData);
	curl_easy_setopt(eh, CURLOPT_WRITEFUNCTION, httpWriteFunc);
	curl_easy_setopt(eh, CURLOPT_VERBOSE, 1);
	curl_easy_setopt(eh, CURLOPT_TIMEOUT, 10);
	curl_easy_setopt(eh, CURLOPT_HTTPHEADER, requestData->chunk);
	//curl_easy_setopt(eh, CURLOPT_FOLLOWLOCATION, 1L);
	curl_easy_setopt(eh, CURLOPT_SSL_VERIFYPEER, 0);
	curl_easy_setopt(eh, CURLOPT_SSL_VERIFYHOST, 0);
	curl_multi_add_handle(cm, eh);

}

/*
为http请求添加头部信息
*/
void prepare_http_head(CURL *eh, HttpRequestData *requestData) {
	struct curl_slist *chunk = NULL;
	char **head_data = requestData->head;
	for (int i = 0; i < requestData->head_num; i++) {
		chunk = curl_slist_append(chunk, *head_data);
		head_data++;
	}
	curl_easy_setopt(eh, CURLOPT_HTTPHEADER, chunk);
	requestData->chunk = chunk;
}
void init_response(HttpRequestData *requestData) {
	requestData->response_size = 0;
	requestData->response = malloc(requestData->response_size + 1);
	requestData->response[0] = '\0';
}
size_t httpWriteFunc(void *ptr, size_t size, size_t nmemb, HttpRequestData *requestData) {
	size_t new_len = requestData->response_size + size * nmemb;
	if (requestData->response_size == 0) {
		requestData->response = malloc(new_len + 1);
	}
	else {
		requestData->response = realloc(requestData->response, new_len + 1);
	}
	memcpy(requestData->response + requestData->response_size, ptr, size*nmemb);
	requestData->response[new_len] = '\0';
	requestData->response_size = new_len;
	return size * nmemb;
}
HttpRequestNode *get_dataFromRequestList(CURL *eh) {
	HttpRequestNode *node = request_list_process.head.next;
	while (node->next != NULL) {
		if (node->data->eh == eh) {
			return node;
		}
		node = node->next;
	}
	return NULL;
}
void httpRequest_solve() {
	if (request_cnt <= 0) {
		return;
	}
	int runing_handle = 0;
	CURLMcode fdset_code, timeout_code;
	fd_set R, W, E;
	long L;
	int M, Q;
	FD_ZERO(&R);
	FD_ZERO(&W);
	FD_ZERO(&E);
	if (fdset_code = curl_multi_fdset(cm, &R, &W, &E, &M)) {
		return;
	}
	struct timeval timeout;
	timeout.tv_sec = 0;
	timeout.tv_usec = 0;
	int num = select(M + 1, &R, &W, &E, &timeout);
	if (num > 0) {
		CURLMcode perform_code = curl_multi_perform(cm, &runing_handle);
		CURLMsg *msg;
		while ((msg = curl_multi_info_read(cm, &Q))) {
			if (msg->msg == CURLMSG_DONE) {
				char *url;
				CURL *e = msg->easy_handle;
				curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &url);
				HttpRequestNode* node = get_dataFromRequestList(e);
				HttpRequestData *data = node->data;
				ircd_log(LOG_ERROR,"httpRequest_solve,data->client:%p",data->client);
				if (msg->data.result == CURLE_OK) {
					long httpcode = 0;
					curl_easy_getinfo(e, CURLINFO_RESPONSE_CODE, &httpcode);
					data->successfinishCallFun(data, httpcode,data->client, data->userPoint);
				}
				else {
					data->failefinishCallFun(data, data->client,data->userPoint);
				}
				requestNode_cleanup(node);
				curl_multi_remove_handle(cm, e);
				curl_easy_cleanup(e);
				request_cnt--;
				httprequest_process();
			}
			else {
				printf("E: CURLMsg (%d)\n", msg->msg);
			}
		}
	}
	else if (num < 0) {
		printf("select %d,request_cnt:%d,ready_length:%d ,error:%i: %s\n", num, request_cnt, get_requestListLength(&request_list_ready), errno, strerror(errno));
		CURLMsg *msg;
		while ((msg = curl_multi_info_read(cm, &Q))) {
			if (msg->msg == CURLMSG_DONE) {
				char *url;
				CURL *e = msg->easy_handle;
				curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &url);
				printf("R: %d - %s <%s>\n",
					msg->data.result, curl_easy_strerror(msg->data.result), url);
				HttpRequestNode* node = get_dataFromRequestList(e);
				HttpRequestData *data = node->data;
				ircd_log(LOG_ERROR,"httpRequest_solve,data->client:%p",data->client);
				if (msg->data.result == CURLE_OK) {
					long httpcode = 0;
					curl_easy_getinfo(e, CURLINFO_RESPONSE_CODE, &httpcode);
					data->successfinishCallFun(data, httpcode,data->client, data->userPoint);
				}
				else {
					data->failefinishCallFun(data,data->client, data->userPoint);
				}
				requestNode_cleanup(node);
				curl_multi_remove_handle(cm, e);
				curl_easy_cleanup(e);
				request_cnt--;
				httprequest_process();
			}
			else {
				printf("E: CURLMsg (%d)\n", msg->msg);
			}
		}
	}

}
/*
清除HttpRequestNode
*/
void requestNode_cleanup(HttpRequestNode* node) {
	requestData_cleanup(node->data);
	delet_httpRequestList(node);
}
/*
清除HttpRequestData
*/
void requestData_cleanup(HttpRequestData *data) {
	curl_slist_free_all(data->chunk);
	for (int i = 0; i < data->head_num; i++) {
		free(data->head[i]);
	}
	free(data->data);
	if (data->error != NULL) {
		//free(data->error);
	}
	free(data->response);
	free(data->url);
	free(data);
}
/*
计算链表的长度
*/
int get_requestListLength(HttpRequestList *list) {
	int length = 0;
	HttpRequestNode *node = request_list_process.head.next;
	while (node->next != NULL) {
		length++;
		node = node->next;
	}
	return length;
}